package com.autohammal.monitor;

import java.io.Serializable;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import com.autohammal.ITarget;
import com.autohammal.ITask;
import com.autohammal.factory.SchedulerFactory;
import com.autohammal.factory.WorkFactory;
import com.autohammal.monitor.scheduler.Scheduler;
import com.autohammal.save.util.NameGengerator;
import com.autohammal.save.util.NameGengerator.SaveType;
import com.autohammal.save.util.NameGengerator.Style;

/**
 * monitor listener
 * 
 * @author dailey_dai Mar 3, 2011
 */
public class SchedulerMonitor extends AbstractMonitor implements Serializable {
	private static final long serialVersionUID = 4006915971337947451L;
	private Scheduler scheduler;

	public SchedulerMonitor(ITarget target, Scheduler scheduler) {
		setTarget(target);
		setScheduler(scheduler);
		target.setMonitor(this);
		if (monitorName == null)
			setMonitorName(NameGengerator.generate(SaveType.MONITOR,
					Style.UUID_TIME));
	}

	public Scheduler getScheduler() {
		return scheduler;
	}

	public void setScheduler(Scheduler scheduler) {
		this.scheduler = scheduler;
	}

	/**
	 * start monitor
	 */
	@Override
	public void start() {
		if (scheduler == null) {
			scheduler = SchedulerFactory.makeScheduler(1000, 1000);
		}
		if (target == null) {
			throw new NullPointerException("Target cannot be null.");
		}
		scheduler.schedule(createSchedulerTask());
		WorkFactory.getWork();
	}

	/**
	 * cancel monitor
	 */
	@Override
	public void cancel() {
		scheduler.cancel();
	}

	/**
	 * create monitor task
	 * 
	 * @return Runnable
	 */
	@Override
	protected Runnable createSchedulerTask() {
		return new Runnable() {
			@Override
			public void run() {
				List<ITask> tasks = null;
				tasks = getTarget().getTasks();
				if (tasks != null) {
					if (monitorListener != null) {
						monitorListener.onGetTasks(Collections
								.unmodifiableList(tasks));
					}
					for (Iterator<ITask> it = tasks.iterator(); it.hasNext();) {
						ITask task = it.next();
						Future<?> future = WorkFactory.getWork().add(task);
						FutureTaskClearThread.newFutureTaskClear(future, task,
								getTarget());
					}
				}
			}

			public String toString() {
				return "Target:" + target.toString() + " at Monitor:"
						+ getMonitorName();
			}

		};
	}

	static class FutureTaskClearThread {
		protected static boolean runstate = true;
		private Future<?> future;
		private ITask task;
		private final ITarget target;
		private final static LinkedBlockingQueue<FutureTaskClearThread> queue = new LinkedBlockingQueue<FutureTaskClearThread>();

		static {
			startClear();
		}

		public static void setRunstate(boolean runstate) {
			FutureTaskClearThread.runstate = runstate;
		}

		protected static void startClear() {
			new Thread() {
				@Override
				public void run() {
					while (runstate) {
						FutureTaskClearThread ftc = null;
						try {
							ftc = queue.take();
							if (ftc.future != null && ftc.future.isDone()) {
								Logger.getLogger(FutureTaskClearThread.class)
										.log(Level.DEBUG,
												"Task["
														+ ftc.task.getTaskId()
														+ "] status:"
														+ ftc.task
																.getTaskStatus());
								ftc.target.remove(ftc.task);
								ftc.task = null;
							} else {
								queue.put(ftc);
								ftc.target.clear();
							}
						} catch (InterruptedException e) {
							Logger.getLogger(FutureTaskClearThread.class).log(
									Level.ERROR, e);
							ftc = null;
						}
					}
				}
			}.start();
		}

		private FutureTaskClearThread(Future<?> future, ITask task,
				ITarget target) {
			this.future = future;
			this.target = target;
			this.task = task;
		}

		public static FutureTaskClearThread newFutureTaskClear(
				Future<?> future, ITask task, final ITarget target) {
			FutureTaskClearThread clear = new FutureTaskClearThread(future,
					task, target);
			try {
				queue.put(clear);
			} catch (InterruptedException e) {
				Logger.getLogger(FutureTaskClearThread.class).log(Level.ERROR,
						e);
			}
			return clear;
		}

	}

}
